/*
一、promise主要逻辑分析:
1.promise是一个class
2.new promise 传递一个excutor执行器函数，该函数立即执行，改变promise状态，状态一旦改变则不可再变
3.promise 状态有三种：pending、fulfilled、rejected
4.promise示例有一个then方法，接收两个参数（resolve回调和reject回调），通过状态判断回调哪个函数
5.then方法可以链式调用（返回promise实例），且前一个then方法的返回值可以作为后一个then方法的回调参数
6.resolve、reject方法可能是异步调用的
7.catch方法可以捕获异常，并可在catch后面链式调用then
8.finally 不管什么状态，都会调用。后面通过then可以拿到最终的value，finally里面可以返回promise对象

二、其他：
1.Promise.all()
2.Promise.resolve()
*/

const PromiseStatus = {
    PENDING: 0,//待处理
    FULFILLED: 1,//成功
    REJECTED: 2//失败
}

class MyPromise {
    constructor(excutor) {
        excutor(this.resolve, this.reject)
    }
    _States = PromiseStatus.PENDING;
    _Value = undefined;
    _Reson = undefined;

    _ResolveCallBacks = [];
    _RejectCallBacks = [];
    resolve = value => {
        if (this._States != PromiseStatus.PENDING) return
        this._States = PromiseStatus.FULFILLED;
        this._Value = value;
        while (this._ResolveCallBacks.length > 0) {
            this._ResolveCallBacks.shift()(this._Value)
        }
    }
    reject = reson => {
        if (this._States != PromiseStatus.PENDING) return
        this._Reson = reson;
        this._States = PromiseStatus.REJECTED;
        while (this._RejectCallBacks.length > 0) {
            this._RejectCallBacks.shift()(this._Reson)
        }
    }
    then(resolve, reject) {
        //处理可选参数问题，防止传参不对，不传参或者少传参报错
        //console.log(typeof (resolve));
        resolve = (resolve && typeof (resolve) == 'function') ? resolve : val => val;
        reject = (reject && typeof (reject) == 'function') ? reject : reson => { throw reson };
        //链式调用，需要返回promise
        let promise = new MyPromise((cResolve, cReject) => {
            if (this._States == PromiseStatus.FULFILLED) {
                setTimeout(() => {
                    //前一个then的返回值
                    let preValue = resolve(this._Value)
                    resolvePromise(promise, preValue, cResolve, cReject)
                }, 0);
            }
            else if (this._States == PromiseStatus.REJECTED) {
                let preValue = reject(this._Reson)
                setTimeout(() => {
                    resolvePromise(promise, preValue, cResolve, cReject)
                }, 0);
            }
            else {
                //promise的构造函数里，异步执行resolve和reject。这个时候_States还是初始值
                //前一个then的返回值
                //这里要try-catch处理，不然不传回调函数会报异常
                this._ResolveCallBacks.push(() => {
                    setTimeout(() => {
                        try {
                            let preValue = resolve(this._Value)
                            resolvePromise(promise, preValue, cResolve, cReject)
                        }
                        catch (e) {
                            cReject(e)
                        }
                    }, 0);
                })
                this._RejectCallBacks.push(() => {
                    setTimeout(() => {
                        try {
                            let preValue = reject(this._Reson)
                            resolvePromise(promise, preValue, cResolve, cReject)
                        }
                        catch (e) {
                            cReject(e)
                        }
                    }, 0);
                })

            }
        });
        return promise;
    }
    /**
     * 接收一个参数，可以是普通值对象，也可以是promise对象。
     * 如果参数是值对象，返回promise对象，并将值对象作为成功回调的参数传入
     * 如果参数是promise对象，直接返回该promise对象
     * @param {object} val 
     * @returns promise
     */
    static resolve(val) {
        if (val instanceof MyPromise) return val;
        return new MyPromise((resolve) => {
            resolve(val)
        })
    }
    /**
     * 接收一个数组，可以是普通值对象，也可以是promise对象。
     * 按照传参顺序，返回调用结果。
     * 如果是值对象，直接返回。如果promise对象，调用then方法的成功回调并返回结果。
     * 异常则在catch中捕获
     * @param {array} arr 
     */
    static all(arr) {
        return new MyPromise((resolve, reject) => {
            let results = [arr.length]
            let i = 1;
            //刚学废的闭包，趁热用一下
            let addData = (index) => {
                return (val) => {
                    results[index] = val;
                    if (arr.length == i) {
                        resolve(results)
                    }
                    i++;
                }
            }
            arr.forEach((el, index) => {
                let fn = addData(index)
                if (el instanceof MyPromise) {
                    el.then(val => fn(val), reson => reject(reson))
                }
                else {
                    fn(el)
                }
            });
        });

    }
    /**
     * 不管成功失败都调用
     * 可以在finally方法之后调用then方法拿到结果
     * @param {fn} callback 
     */
    finally(callback) {
        return this.then(res => {
            return callback(res)
        }, reson => {
            return callback(reson)
        })
    }

    /**
     * 捕获promise异常，并将异常信息作为callback函数的参数
     * catch之后还可以链式调用,拿到返回的结果
     * @param {fn} callback 
     */
    catch(callback) {
        return this.then(null, (reson) => {
            return callback(reson)
        })
    }
}
function resolvePromise(promise, preValue, resolve, reject) {
    // 如果相等了，说明return的是自己，抛出类型错误并返回
    if (promise === preValue) {
        return reject(new TypeError('Chaining cycle detected for promise #<Promise>'))
    }
    //如果前一个then返回的是promise 调用promise的then方法
    //如果前一个then返回的是普通值，直接调用resolve方法
    if (preValue instanceof MyPromise) {
        preValue.then(resolve, reject)
    } else {
        resolve(preValue)
    }
}

module.exports = MyPromise;